package service

import (
	"os"
	"os/signal"
	"sync"
	"syscall"
	"time"

	"gameserver/gcommon"
	"gameserver/messages"
	"gameserver/server/Login/internal"
	"gameserver/server/Login/internal/slistmgr"

	"gitee.com/night-tc/gobige/common"
	"gitee.com/night-tc/gobige/dblink"
	"gitee.com/night-tc/gobige/msghandler"
	"gitee.com/night-tc/gobige/service"
	"github.com/gin-gonic/gin"
	"go.uber.org/atomic"

	"gitee.com/night-tc/gobige/entity"
	"gitee.com/night-tc/gobige/etcd"
	"gitee.com/night-tc/gobige/ginhttp"
	"gitee.com/night-tc/gobige/global"
	"gitee.com/night-tc/gobige/logger"
	"gitee.com/night-tc/gobige/redislib"
	"gitee.com/night-tc/gobige/serverMgr"
	"gitee.com/night-tc/gobige/threads"
	"gitee.com/night-tc/gobige/timerwheel"
)

/*
Login服务器的服务部分
这个服务器是已HTTP为基础的服务
用来处理用户的登录逻辑；
分配Getway地址；
*/
type LoginService struct {
	*ginhttp.HttpMgr
	*msghandler.MsgHandler
	entity.DelayCallHost
	*timerwheel.Timer

	// eid生成器
	eIDGenerater *service.IDGenerater
	//协程管理
	thgo *threads.ThreadGo
	//服务器关闭信号
	closeC chan os.Signal
	//每次Tick的时候的当前时间
	NowTime atomic.Time

	//服务器信息
	Sinfo *gcommon.ServiceConfig
	//etcd
	Etcd *etcd.EtcdAccess
	//服务器信息管理器
	DisMgr *serverMgr.DiscoveryMgr
	//数据库管理器
	MysqlMgr *dblink.MysqlAccess
	// 链接linker服务器的管理器
	LinkerMgr *internal.LinkerSrvMgr

	// 所有等回复的消息
	WaitList sync.Map
}

func NewLoginService(sinfo *gcommon.ServiceConfig, eng *gin.Engine) (result *LoginService) {
	result = new(LoginService)
	//设置服务器实例
	global.SetSrvInst(result)
	result.thgo = threads.NewThreadGo()
	// logger.Init(sinfo.LogFile, sinfo.SrvName, nil)
	result.eIDGenerater = service.NewIDGenerater(sinfo.GetServerID())
	result.Sinfo = sinfo
	internal.LoginSalt = sinfo.LoginToken
	internal.Jwkey = []byte(sinfo.LoginToken)
	result.closeC = make(chan os.Signal, 1)
	result.MsgHandler = msghandler.NewMsgHandler()
	result.HttpMgr = ginhttp.NewHttpMgr(result.thgo, sinfo.ListenMap[global.ListenType_Http], eng)
	result.NowTime.Store(time.Now())
	if result.Timer == nil {
		result.Timer = timerwheel.NewTimer()
	}
	if result.Etcd == nil {
		var err error
		result.Etcd, err = etcd.NewEtcdAccess(
			etcd.SetEtcdAddrs(sinfo.EtcdCfg.Addrs...),
			etcd.SetEtcdDialTimeout(sinfo.EtcdCfg.DialTimeout),
			etcd.SetEtcdLeaseTTL(sinfo.EtcdCfg.LeaseTTL),
		)
		if err != nil {
			panic(err)
		}
	}

	if result.DisMgr == nil {
		result.DisMgr = serverMgr.NewDiscoveryMgr(result.thgo, result)
	}

	if result.MysqlMgr == nil {
		result.MysqlMgr = dblink.NewMysqlAccess(
			dblink.SetMysqlAccessByConfig(&sinfo.MysqlConfig),
		)
	}

	if result.LinkerMgr == nil {
		result.LinkerMgr = internal.NewLinkerSrvMgr()
	}

	//实例化redis
	for name, v := range sinfo.RedisMap {
		rd := redislib.NewRedisAccess(
			redislib.RedisSetAddr(v.Addr),
			redislib.RedisSetAuth(v.Password),
			redislib.RedisSetIdleTimeout(time.Duration(v.IdleTimeout)*time.Second),
			redislib.RedisSetIndexDB(v.Index),
			redislib.RedisSetMaxActive(v.MaxActive),
			redislib.RedisSetMaxIdle(v.MaxIdle),
		)
		global.SetRedisByName(name, rd)

	}
	return
}

func (this *LoginService) Init() (err error) {

	signal.Notify(this.closeC, syscall.SIGINT, syscall.SIGTERM)
	if this.Sinfo.IsCreateDB {
		_ = this.MysqlMgr.AutoMigrate(
			&messages.Area_MD{},
			&messages.Group_MD{},
			&messages.Account_MD{},
			&messages.Player_MD{},
			&messages.Whitelist_MD{},
		)

	}

	// if err = this.DisMgr.Start(); err != nil {
	// 	return err
	// }
	this.Timer.Start()
	if err = this.HttpMgr.Start(); err != nil {
		return err
	}
	this.WatchServer()
	// 初始化服务器列表管理器
	slistmgr.NewServerListMgr()
	return nil
}

// 监听需要主动连接的服务器逻辑
func (this *LoginService) WatchServer() {
	/*
	   如果需要对某子服务器的分配器，做一些修改，可以在这里设置
	   Watch函数对同一个类型的只能Watch一次
	   做服发现一般有二个目的：
	   1、主动连接对方服务器；
	   2、管理用户去对应服务器时候，使用什么负载均衡的逻辑；
	*/
	this.DisMgr.WatchByStype(global.ServerTypeGateway, serverMgr.NewServerMapByGway())
	for _, stype := range this.Sinfo.ConSrvTypeList {
		this.DisMgr.WatchByStype(stype, nil)
	}
	for _, stype := range this.Sinfo.WatchSrvTypeList {
		this.DisMgr.WatchByStype(stype, nil)
	}
	for _, stype := range this.Sinfo.OthConSrvTypeList {
		this.DisMgr.WatchByStype(stype, nil)
	}
	for _, stype := range this.Sinfo.OthWatchSrvTypeList {
		this.DisMgr.WatchByStype(stype, nil)
	}
}

func (this *LoginService) Run() {
	/*
		这个函数在主协程上运行

		一些需要启动的管理器

		这个函数不能panic所以调用上层代码的时候，都需要有recover的保护

	*/
	// global.PrintConfig()
	logger.Infof("\n BuildTime : %s\n Version : %s\n Gitcommit : %s", global.Built, global.Version, global.Gitcommit)
	if err := this.Init(); err != nil {
		logger.Errorf("Service.Run err:%v", err)
		return
	}
	logger.Infof("Service %s Runing.", this.Sinfo.SrvName)
	service.SetTitle(this.Sinfo.SrvName)
	logger.Info(common.LOGO)
	this.Etcd.Start()

	tick := time.NewTicker(this.Sinfo.FrameDeltaTime)
	defer tick.Stop()
	for {
		select {
		case <-this.closeC:
			//关服
			logger.Infof("Service %s Stoping.", this.Sinfo.SrvName)
			this.HttpMgr.Stop()
			this.Etcd.Close()
			this.Timer.Stop()
			this.thgo.CloseWait()
			logger.Info(common.Stop)
			return
		case <-this.GetCallChan():
			this.DoMsg()
		case nowtime := <-tick.C:
			this.NowTime.Store(nowtime)
			this.DoMsg()
			this.mainLoop()
		}
	}
}

func (this *LoginService) mainLoop() {
	defer func() {
		if global.GetCfgInst().GetRecover() {
			if err := recover(); err != nil {
				logger.Error(err)
				buf := common.StackTrace()
				common.OnRecover(err, buf, this)

			}
		}
	}()
	this.Timer.OnTick(this.NowTime.Load())

	this.DelayCallHost.Exec()

}
